package com.javasm.adminapi.common.config;

import com.alibaba.fastjson2.JSON;
import com.javasm.adminapi.common.R;
import com.javasm.adminapi.module.system.dto.MenuAndUrl;
import com.javasm.adminapi.module.system.dto.MenuTypeDTO;
import com.javasm.adminapi.module.system.entity.Permission;
import com.javasm.adminapi.module.system.entity.SysPermission;
import com.javasm.adminapi.module.system.service.PermissionService;
import com.javasm.adminapi.module.system.service.SysPermissionService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @className: SpringSecurity
 * @description:
 * @author: zcx
 * @date: 2025/10/10 17:41
 * @version: 0.1
 * @since: jdk17
 */
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SpringSecurity extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //********************跨域***************************//
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        //配置跨域
//        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsFilter(source);
    }


    @Resource
    SysPermissionService sysPermissionService;
    @Resource
    PermissionService permissionService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {


        http.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class);
//        http.authorizeRequests()
//                .antMatchers("/login/**","/**")
//                .permitAll()
//                .anyRequest().authenticated();//其他的请求，都必须经过认证才能访问

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry =
                http.authorizeRequests().antMatchers("/login/**", "/javasmlogin") // 注意：登录接口必须放行
                .permitAll();

        // 3.1 遍历数据库权限，为每个 URL 配置对应的权限要求
//        List<MenuAndUrl> list = sysPermissionService.getMenuAndUrl();

        List<MenuTypeDTO> urlList = permissionService.getMenuType(1L);

        for (MenuTypeDTO permission : urlList) {
            String url = permission.getApiUrl();
            String menu = permission.getCode()+":"+permission.getApiType();

            System.out.println("接口路径：" + url + "，所需权限标识：" + menu);
            registry.antMatchers(url).hasAuthority(menu);

        }

                // 3.3 其他未配置的请求：必须登录（认证通过）
        registry.anyRequest().denyAll();

        //表单提交登录的配置
        http.formLogin()
                .usernameParameter("uname")//配置页面表单中，用户名的name属性值
                .passwordParameter("pwd")//配置页面表单中，密码的name属性
                .loginProcessingUrl("/javasmlogin")//表单提交的action地址，注意：/不能忘记
                //登录成功之后，执行的方法
                .successHandler((request, response, authentication) -> printSuccess(response, authentication))
                // 抛出异常，执行的方法，AuthenticationException是异常的信息内容
                .failureHandler((request, response, e) -> printFailure(response, e.getMessage()))
                .permitAll();//以上的路径，不登录都能访问
        //未登录
        http.exceptionHandling().authenticationEntryPoint((request, response, e) -> printFailure(response, "未登录"));
        //403
        http.exceptionHandling().accessDeniedHandler((request,response,e)-> printFailure(response,"没有权限访问"));
        //退出登录
        http.logout().logoutUrl("/logout").logoutSuccessHandler((request, response, a) -> printFailure(response, "退出登录成功"));

        //关闭csrf，访问跨域伪造，//这个不关闭的话，无法访问无法登录
        http.csrf().disable();

    }

    private void printSuccess(HttpServletResponse response, Authentication authentication) {
        try {
            response.setContentType("application/json;charset=utf-8");
            R ok = R.ok(authentication);
            PrintWriter printWriter = response.getWriter();
            printWriter.write(JSON.toJSONString(ok));
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void printFailure(HttpServletResponse response, String msg) {
        try {
            response.setContentType("application/json;charset=utf-8");
            R error = new R();
            error.setCode(500);
            error.setMsg(msg);
            PrintWriter printWriter = response.getWriter();
            printWriter.write(JSON.toJSONString(error));
            printWriter.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //也能访问一部分请求
        //这里配置的地址，是绕开Security框架直接访问
        web.ignoring().antMatchers("/js/*", "*.css", "/book/**");
    }
}
